/**
* @file formatter.hpp
* @brief 将日志进行格式化的输出，用到了工厂模式
* @author zhaobohan (zhaobohan_free@163.com)
*/

#ifndef __M_FMT_H__
#define __M_FMT_H__

#include "util.hpp"
#include "message.hpp"
#include "level.hpp"
#include <memory>
#include <vector>
#include <tuple>

namespace mylogs {

// 一个抽象类，定义了一个 format 方法的纯虚函数，所有具体的格式化类都继承自这个类
class FormatItem
{
public:
    using ptr = std::shared_ptr<FormatItem>;
    virtual ~FormatItem() {}
    virtual void format(std::ostream &os, const LogMsg &msg) = 0;
};

// 输入日志消息
class MsgFormatItem : public FormatItem 
{
public:
    MsgFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << msg._payload;
    }
};

// 输入日志等级
class LevelFormatItem : public FormatItem 
{
public:
    LevelFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << LogLevel::toString(msg._level);
    }
};

// 输入日志器的名称
class NameFormatItem : public FormatItem 
{
public:
    NameFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << msg._name;
    }
};

// 线程ID
class ThreadFormatItem : public FormatItem 
{
public:
    ThreadFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << msg._tid;
    }
};

// 当前日志的时间
class TimeFormatItem : public FormatItem 
{
private:
    std::string _format;

public:
    TimeFormatItem(const std::string &format = "%H:%M:%S")
        :_format(format)
    {
        if (format.empty()) 
            _format = "%H:%M:%S";
    }

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        time_t t = msg._ctime;
        struct tm lt;
        localtime_r(&t, &lt);
        char tmp[128];
        strftime(tmp, 127, _format.c_str(), &lt);
        os << tmp;
    }
};

// 输入当前日志所属的文件
class CFileFormatItem : public FormatItem 
{
public:
    CFileFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << msg._file;
    }
};

// 输入当前所属的行号
class CLineFormatItem : public FormatItem 
{
public:
    CLineFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << msg._line;
    }
};

// 输入一个换行符
class TabFormatItem : public FormatItem 
{
public:
    TabFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << "\t";
    }
};

// 输入一个换行符
class NLineFormatItem : public FormatItem 
{
public:
    NLineFormatItem(const std::string &str = "")
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << "\n";
    }
};

// 输入其他的信息
class OtherFormatItem : public FormatItem 
{
private:
    std::string _str;

public:
    OtherFormatItem(const std::string &str = "")
        :_str(str)
    {}

    virtual void format(std::ostream &os, const LogMsg &msg) 
    {
        os << _str;
    }
};

// 进行格式化输出
class Formatter 
{
public:
    using ptr = std::shared_ptr<Formatter>;

    // 时间{年-月-日 时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志名称] 缩进 文件名:行号 缩进 消息换行
    Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n")
        : _pattern(pattern)
    {
        assert(parsePattern());
    }

    const std::string pattern() 
    { 
        return _pattern; 
    }

    std::string format(const LogMsg &msg) 
    {
        std::stringstream ss;
        for (auto &it : _items) 
        {
            it->format(ss, msg);
        }
        return ss.str();
    }

    std::ostream& format(std::ostream &os, const LogMsg &msg) 
    {
        for (auto &it : _items) 
        {
            it->format(os, msg);
        }
        return os;
    }

    // 根据传入的格式化字符和子格式字符串来创建并返回一个具体的 FormatItem 对象
    FormatItem::ptr createItem(const std::string &fc, const std::string &subfmt) 
    {
        if (fc == "m") 
            return FormatItem::ptr(new MsgFormatItem(subfmt));
        if (fc == "p") 
            return FormatItem::ptr(new LevelFormatItem(subfmt));
        if (fc == "c") 
            return FormatItem::ptr(new NameFormatItem(subfmt));
        if (fc == "t") 
            return FormatItem::ptr(new ThreadFormatItem(subfmt));
        if (fc == "n") 
            return FormatItem::ptr(new NLineFormatItem(subfmt));
        if (fc == "d") 
            return FormatItem::ptr(new TimeFormatItem(subfmt));
        if (fc == "f") 
            return FormatItem::ptr(new CFileFormatItem(subfmt));
        if (fc == "l") 
            return FormatItem::ptr(new CLineFormatItem(subfmt));
        if (fc == "T") 
            return FormatItem::ptr(new TabFormatItem(subfmt));
        return FormatItem::ptr();
    }

    // pattern解析 
    bool parsePattern() 
    {
        //std::string _pattern = "sg{}fsg%d{%H:%M:%S}%Tsdf%t%T[%p]%T[%c]%T%f:%l%T%m%n  ";
        //std::cout << _pattern << std::endl;
        //每个要素分为三部分：
        //  格式化字符  : %d  %T  %p...
        //  对应的输出子格式 ： {%H:%M:%S}
        //  对应数据的类型 ： 0-表示原始字符串，也就是非格式化字符，1-表示格式化数据类型
        //  默认格式 "%d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n"
        std::vector<std::tuple<std::string, std::string, int>> arry;
        std::string format_key;//存放%后的格式化字符
        std::string format_val;//存放格式化字符后边 {} 中的子格式字符串
        std::string string_row;//存放原始的非格式化字符
        bool sub_format_error = false;
        int pos = 0;
        while (pos < _pattern.size()) 
        {
            if (_pattern[pos] != '%') 
            {
                string_row.append(1, _pattern[pos++]);
                continue;
            }

            if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%') 
            {
                string_row.append(1, '%');
                pos += 2;
                continue;
            }

            if (string_row.empty() == false) 
            {
                arry.push_back(std::make_tuple(string_row, "", 0));
                string_row.clear();
            }

            // 当前位置是%字符位置
            pos += 1;// pos指向格式化字符位置
            if (pos < _pattern.size() && isalpha(_pattern[pos])) 
            {
                // 保存格式化字符
                format_key = _pattern[pos];
            }
            else 
            {
                std::cout << &_pattern[pos-1] << "位置附近格式错误！\n";
                return false;
            }

            // pos指向格式化字符的下一个位置，判断是否包含有子格式 %d{%Y-%m-%d}
            pos += 1;
            if (pos < _pattern.size() && _pattern[pos] == '{') 
            {
                sub_format_error = true;
                pos += 1;//pos指向花括号下一个字符处
                while(pos < _pattern.size()) 
                {
                    if (_pattern[pos] == '}') 
                    {
                        sub_format_error = false;
                        pos += 1;//让pos指向}的下一个字符处
                        break;
                    }

                    format_val.append(1, _pattern[pos++]);
                }
            }
            arry.push_back(std::make_tuple(format_key, format_val, 1));
            format_key.clear();
            format_val.clear();
        }

        if (sub_format_error) 
        {
            std::cout << "{}对应出错\n";
            return false;
        }

        if (string_row.empty() == false) 
            arry.push_back(std::make_tuple(string_row, "", 0));
        if (format_key.empty() == false) 
            arry.push_back(std::make_tuple(format_key, format_val, 1));
        for (auto &it : arry) 
        {
            if (std::get<2>(it) == 0) 
            {
                FormatItem::ptr fi(new OtherFormatItem(std::get<0>(it)));
                _items.push_back(fi);
            }
            else 
            {
                FormatItem::ptr fi = createItem(std::get<0>(it), std::get<1>(it));
                if (fi.get() == nullptr) 
                {
                    std::cout << "没有对应的格式化字符: %" <<  std::get<0>(it) << std::endl;
                    return false;
                }
                _items.push_back(fi);
            }
        }
        return true;
    }

private:
    std::string _pattern;
    std::vector<FormatItem::ptr> _items;
};

}
#endif